/**
 * @file	flash.c
 * @author	chipsea
 * @brief	Contains all functions support for flash driver
 * @version	0.1
 * @date	2020-11-30
 * @copyright Copyright (c) 2020, CHIPSEA Co., Ltd.
 * @note
 */
#include "sdk_config.h"
#include "flash.h"
#include "log.h"
#include "types.h"
#include "error.h"
#include "pwrmgr.h"
#include <string.h>
#include "rom_sym_def.h"


#define FLASH_LOCK_504K         0x04//0x00-0x7DFFF
#define FLASH_LOCK_496K         0x08//0x00-0x7BFFF
#define FLASH_LOCK_480K         0x0C//0x00-0x77FFF
#define FLASH_LOCK_448K         0x10//0x00-0x6FFFF
#define FLASH_LOCK_384K         0x14//0x00-0x5FFFF
#define FLASH_LOCK_HALF         0x18//0x00-0x3FFFF
#define FLASH_LOCK_ALL          0x1C//0x00-0x7FFFF
#define FLASH_UNLOCK            0x00

#define WRITE_ENABLE            0x6000001
#define WRITE_PROTECT           0x1008001
#define REG_FLASH_BP            (*((volatile unsigned int *)(0x4000c8a8)))
#define REG_FLASH_WRITE_CTRL    (*((volatile unsigned int *)(0x4000c890)))


#define SPIF_WAIT_IDLE_CYC                          (32)

#define SPIF_STATUS_WAIT_IDLE(n)                    \
    do                                              \
    {                                               \
        while((AP_SPIF->fcmd &0x02)==0x02);         \
        {                                           \
            volatile int delay_cycle = n;           \
            while (delay_cycle--){;}                \
        }                                           \
        while ((AP_SPIF->config & 0x80000000) == 0);\
    } while (0);

	
#define HAL_CACHE_ENTER_BYPASS_SECTION()  do{ \
	HAL_ENTER_CRITICAL_SECTION();\
	AP_CACHE->CTRL0 = 0x02; \
	AP_PCR->CACHE_RST = 0x02;\
	AP_PCR->CACHE_BYPASS = 1;    \
	HAL_EXIT_CRITICAL_SECTION();\
}while(0);


#define HAL_CACHE_EXIT_BYPASS_SECTION()  do{ \
	HAL_ENTER_CRITICAL_SECTION();\
	AP_CACHE->CTRL0 = 0x00;\
	AP_PCR->CACHE_RST = 0x03;\
	AP_PCR->CACHE_BYPASS = 0;\
	HAL_EXIT_CRITICAL_SECTION();\
}while(0);

#define spif_wait_nobusy(flg, tout_ns, return_val)   {if(_spif_wait_nobusy_x(flg, tout_ns)){if(return_val){ return return_val;}}}

typedef struct {
    sysclk_t spif_ref_clk;
    uint32_t rd_instr;
}xflash_Ctx_t;
static xflash_Ctx_t s_xflashCtx = {.spif_ref_clk=_MCU_SPIF_CLK_SEL,.rd_instr=XFRD_FCMD_READ_DUAL};

extern int _spif_wait_nobusy(uint8_t flg, uint32_t tout_ns);
extern int  spif_write(uint32_t addr, uint8_t* data, uint32_t size);
extern int  spif_write_dma(uint32_t addr, uint8_t* data, uint32_t size);
extern int  spif_read(uint32_t addr, uint8_t* data, uint32_t size);
extern int  spif_read_dma(uint32_t addr, uint8_t* data, uint32_t size);
extern int spif_erase_sector(unsigned int addr);
extern int spif_erase_block64(unsigned int addr);
extern int spif_erase_all(void);
extern uint8_t spif_flash_status_reg_0(void);
extern int spif_write_protect(bool en);
extern void spif_cmd(uint8_t op, uint8_t addrlen, uint8_t rdlen, uint8_t wrlen, uint8_t mbit, uint8_t dummy);
extern void spif_rddata(uint8_t* data, uint8_t len);
extern int spif_config(sysclk_t ref_clk, uint8_t div,  uint32_t rd_instr,  uint8_t mode_bit, uint8_t QE);

static inline uint32_t spif_lock()
{
    HAL_ENTER_CRITICAL_SECTION();
    uint32_t vic_iser = NVIC->ISER[0];
    //mask all irq
    NVIC->ICER[0] = 0xFFFFFFFF;
    //enable ll irq and tim1 irq
    NVIC->ISER[0] = 0x100010;
    HAL_EXIT_CRITICAL_SECTION();
    return vic_iser;
}

static inline void spif_unlock(uint32_t vic_iser)
{
    HAL_ENTER_CRITICAL_SECTION();
    NVIC->ISER[0] = vic_iser;
    HAL_EXIT_CRITICAL_SECTION();
}

static void hal_cache_tag_flush(void)
{
    HAL_ENTER_CRITICAL_SECTION();
    uint32_t cb = AP_PCR->CACHE_BYPASS;
    volatile int dly = 8;
    if(cb==0)
	{		
        AP_PCR->CACHE_BYPASS = 1;
	}
	AP_CACHE->CTRL0 = 0x02; 
    while (dly--){;}; 
    AP_CACHE->CTRL0 = 0x03;             
    dly = 8;while (dly--){;};  
	AP_CACHE->CTRL0 = 0x00;   
    if(cb==0)
	{
        AP_PCR->CACHE_BYPASS = 0;
	}
	
    HAL_EXIT_CRITICAL_SECTION();
}

static uint8_t _spif_read_status_reg_x(void)
{
  uint8_t status;
  spif_cmd(FCMD_RDST, 0, 2, 0, 0, 0);
  SPIF_STATUS_WAIT_IDLE(SPIF_WAIT_IDLE_CYC);
  spif_rddata(&status, 1);
  return status;
}

static int _spif_wait_nobusy_x(uint8_t flg, uint32_t tout_ns)
{
  uint8_t status;
  volatile int tout = (int )(tout_ns);
  for(; tout ; tout --){
    status = _spif_read_status_reg_x();
    if((status & flg) == 0)
      return ERR_NONE;

    //insert polling interval
    //5*32us
    WaitRTCCount(5);
  }
  return ERR_BUSY;
}

static void hal_cache_init(void)
{
    volatile int dly=100;
    //clock gate
    hal_clk_gate_enable(MOD_HCLK_CACHE);
    hal_clk_gate_enable(MOD_PCLK_CACHE);

    //cache rst ahp
    AP_PCR->CACHE_RST=0x02;
    while(dly--){};
    AP_PCR->CACHE_RST=0x03;
    hal_cache_tag_flush();
    //cache enable
    AP_PCR->CACHE_BYPASS = 0;
}

static void hw_spif_cache_config(void)
{
    spif_config(s_xflashCtx.spif_ref_clk,/*div*/1,s_xflashCtx.rd_instr,0,0);
    AP_SPIF->wr_completion_ctrl=0xff010005;//set longest polling interval
    NVIC_DisableIRQ(SPIF_IRQn);              
    NVIC_SetPriority((IRQn_Type)SPIF_IRQn, IRQ_PRIO_HAL);              
    hal_cache_init();
}

int hal_flash_erase_all(void)
{
    uint8_t retval;
    uint32_t cb = AP_PCR->CACHE_BYPASS;
	HAL_CACHE_ENTER_BYPASS_SECTION();
    SPIF_STATUS_WAIT_IDLE(SPIF_WAIT_IDLE_CYC);
    spif_wait_nobusy(SFLG_WIP, SPIF_TIMEOUT, ERR_BUSY);
    __disable_irq();
    retval = spif_erase_all();
    __enable_irq();
	SPIF_STATUS_WAIT_IDLE(SPIF_WAIT_IDLE_CYC);
    spif_wait_nobusy(SFLG_WELWIP, SPIF_TIMEOUT, ERR_BUSY);
	HAL_CACHE_EXIT_BYPASS_SECTION();
    if(cb == 0)
    {
        hal_cache_tag_flush();
    }
    return retval;
}

/**
* @fn void HalFlashInit(void)
* @brief flash Initialization function.
* @param NONE.
* @return NONE.
*/
ErrCode_t HalFlashInit(void)
{
    hw_spif_cache_config();

    hal_pwrmgr_register(MOD_SPIF, NULL,  hw_spif_cache_config);

    return ERR_NONE; 
}

/**
* @fn void HalFlashRead(uint32_t offset, uint8_t *buf, uint32_t len)
* @brief flash read function.
* @param[offset] read address in flash.
* @param[buf] receive data buffer.
* @param[len] receive data length.
* @return NONE.
*/
void HalFlashRead(uint32_t addr, uint8_t *buf, uint32_t len)
{
    volatile uint8_t *u8_spif_addr = (volatile uint8_t *)((addr & 0x7ffff) | FLASH_BASE_ADDR);
    uint32_t cb = AP_PCR->CACHE_BYPASS;

    uint32_t cs = spif_lock();
    
#if(SPIF_FLASH_SIZE==FLASH_SIZE_1MB)
    uint32_t remap = addr & 0xf80000;
    if(remap)
    {
        AP_SPIF->remap = remap;
        AP_SPIF->config |= 0x10000;
    }
#endif

    //read flash addr direct access    
    //bypass cache
    if(cb == 0)
	{
		HAL_CACHE_ENTER_BYPASS_SECTION();
	}
	
    for(int i=0; i<len; i++)
    {
        buf[i]=u8_spif_addr[i];
    }
    //bypass cache
    if(cb == 0)
	{
		HAL_CACHE_EXIT_BYPASS_SECTION();
	}

#if(SPIF_FLASH_SIZE==FLASH_SIZE_1MB)
    if(remap)
    {
        AP_SPIF->remap = 0;
        AP_SPIF->config &= ~0x10000ul;
    }
#endif
    spif_unlock(cs);
}

/**
* @fn ErrCode_t HalFlashWrite(uint32_t offset, uint8_t *buf, uint16_t len)
* @brief flash write function.(offset + len) must be smaller than the current sector
* @param offset: write address in flash.
* @param buf: write data buffer.
* @param len: write data length.
* @return ErrCode_t:return ERR_INVALID_PARAM when the input parameter is incorrect;
					return ERR_NONE when the operation is complete;
*/
ErrCode_t HalFlashWrite(uint32_t addr, uint8_t* buf, uint32_t len)
{
	ErrCode_t retval;
	
	if((buf == NULL) || (len == 0))
		return ERR_INVALID_DATA;

    addr &= 0x00FFFFFF;
    
    if( (addr < 0x2000) || (addr > 0x7FFFF) || ((addr+len) > 0x80000) )
    {
        return ERR_INVALID_PARAM;
    }
	if(len > 4096)
	{
		return ERR_INVALID_LENGTH;
	}

    uint32_t cs = spif_lock();
    
	HAL_CACHE_ENTER_BYPASS_SECTION();
	
    SPIF_STATUS_WAIT_IDLE(SPIF_WAIT_IDLE_CYC);
    spif_wait_nobusy(SFLG_WIP, SPIF_TIMEOUT, ERR_BUSY);
    __disable_irq();
    retval = (ErrCode_t)spif_write(addr, buf, len);
	__enable_irq();
    SPIF_STATUS_WAIT_IDLE(SPIF_WAIT_IDLE_CYC);
    spif_wait_nobusy(SFLG_WIP, SPIF_TIMEOUT, ERR_BUSY);
    
	HAL_CACHE_EXIT_BYPASS_SECTION();

    spif_unlock(cs);
    
    return retval;
}
/**
* @fn ErrCode_t HalFlashWrite(uint32_t offset, uint8_t *buf, uint16_t len)
* @brief flash write function.(offset + len) must be smaller than the current sector
* @param offset: write address in flash.
* @param buf: write data buffer.
* @param len: write data length.
* @return ErrCode_t:return ERR_INVALID_PARAM when the input parameter is incorrect;
					return ERR_NONE when the operation is complete;
*/
ErrCode_t HalFlashWriteByDma(uint32_t addr, uint8_t* buf, uint32_t len)
{
	ErrCode_t retval;
	if(buf == NULL)
	{
		return ERR_INVALID_DATA;
	}
	if((len == 0) || (len > 4096))
	{
		return ERR_INVALID_LENGTH;
	}
    uint32_t cs = spif_lock();
    addr &= 0x00FFFFFF;
    if( (addr < 0x2000) || (addr > 0x7FFFF) || ((addr+len) > 0x80000) )
    {
        return ERR_INVALID_PARAM;
    }

	HAL_CACHE_ENTER_BYPASS_SECTION();
	
    SPIF_STATUS_WAIT_IDLE(SPIF_WAIT_IDLE_CYC);
    spif_wait_nobusy(SFLG_WIP, SPIF_TIMEOUT, ERR_BUSY);
    __disable_irq();
    retval = (ErrCode_t)spif_write_dma(addr, buf, len);
    __enable_irq();
    SPIF_STATUS_WAIT_IDLE(SPIF_WAIT_IDLE_CYC);
    spif_wait_nobusy(SFLG_WIP, SPIF_TIMEOUT, ERR_BUSY);
    
	HAL_CACHE_EXIT_BYPASS_SECTION();
    spif_unlock(cs);
    return retval;
}

/**
* @fn ErrCode_t HalFlashWriteWord(uint32_t offset, uint32_t value)
* @brief flash write function.(offset + len) must be smaller than the current sector
* @param offset: write address in flash.
* @param value: write data.
* @return ErrCode_t:return ERR_INVALID_PARAM when the input parameter is incorrect;
					return ERR_NONE when the operation is complete;
*/
ErrCode_t HalFlashWriteWord(uint32_t offset, uint32_t  value)
{
    offset &= 0x00FFFFFF;
    
    if( (offset < 0x2000) || (offset > 0x7FFFC) )
    {
        return ERR_INVALID_PARAM;
    }

	uint32_t temp = value;
    return (HalFlashWrite(offset, (uint8_t *) &temp, 4)); 	
}

/**
* @fn ErrCode_t HalFlashErase(uint32_t sectorNum)
* @brief erase one sector(4K byte) from falsh.
* @param[sectorNum] erase sector number.
* @return ErrCode_t:return ERR_INVALID_PARAM when the input parameter is incorrect;
					return ERR_NONE when the operation is complete;
*/
ErrCode_t HalFlashErase(uint32_t sectorNum)
{
    sectorNum &= 0x00000FFF;
    if( (sectorNum < 2) || (sectorNum > 0x7F) )
    {
        return ERR_INVALID_PARAM;
    }
    
    uint8_t status = _spif_read_status_reg_x() & FLASH_LOCK_ALL;
    if(status)
    {
        switch(status)
        {
            case FLASH_LOCK_504K:
                if(sectorNum > 0x7D)
                {
                    return ERR_INVALID_PARAM;
                }
            break;
            
            case FLASH_LOCK_496K:
                if(sectorNum > 0x7B)
                {
                    return ERR_INVALID_PARAM;
                }
            break;
            
            case FLASH_LOCK_480K:
                if(sectorNum > 0x77)
                {
                    return ERR_INVALID_PARAM;
                }
            break;
            
            case FLASH_LOCK_448K:
                if(sectorNum > 0x6F)
                {
                    return ERR_INVALID_PARAM;
                }
            break;
            
            case FLASH_LOCK_384K:
                if(sectorNum > 0x5F)
                {
                    return ERR_INVALID_PARAM;
                }
            break;
            
            case FLASH_LOCK_HALF:
                if(sectorNum > 0x3F)
                {
                    return ERR_INVALID_PARAM;
                }
            break;
            
            case FLASH_LOCK_ALL:
                return ERR_INVALID_PARAM;
        }
    }
    
    ErrCode_t retval;
    uint32_t cs = spif_lock();
    
    uint32_t cb = AP_PCR->CACHE_BYPASS;
	HAL_CACHE_ENTER_BYPASS_SECTION();
    SPIF_STATUS_WAIT_IDLE(SPIF_WAIT_IDLE_CYC);
    spif_wait_nobusy(SFLG_WIP, SPIF_TIMEOUT, ERR_BUSY);
    __disable_irq();
    retval = (ErrCode_t)spif_erase_sector((sectorNum<<12));//retval = (ErrCode_t)spif_erase_sector(0x1107b000);
    __enable_irq();
	SPIF_STATUS_WAIT_IDLE(SPIF_WAIT_IDLE_CYC);
    spif_wait_nobusy(SFLG_WELWIP, SPIF_TIMEOUT, ERR_BUSY);
	HAL_CACHE_EXIT_BYPASS_SECTION();
    if(cb == 0)
    {
        hal_cache_tag_flush();
    }
    spif_unlock(cs);
    
    return retval;
}

/**
* @fn ErrCode_t HalFlashErase64K(uint32_t addr)
* @brief erase (64 Kbyte) from falsh.
* @param[addr] erase addr.
* @return ErrCode_t:return ERR_INVALID_PARAM when the input parameter is incorrect;
					return ERR_NONE when the operation is complete;
*/
ErrCode_t HalFlashErase64K(uint32_t addr)
{
    addr &= 0x00FFFFFF;
    if( (addr < 0x10000) || (addr > 0x70000) )
    {
        return ERR_INVALID_PARAM;
    }

    ErrCode_t retval;
    uint32_t cs = spif_lock();
    uint32_t cb = AP_PCR->CACHE_BYPASS;
	HAL_CACHE_ENTER_BYPASS_SECTION();
    SPIF_STATUS_WAIT_IDLE(SPIF_WAIT_IDLE_CYC);
    spif_wait_nobusy(SFLG_WIP, SPIF_TIMEOUT, ERR_BUSY);
    __disable_irq();
    retval = (ErrCode_t)spif_erase_block64(addr);
    __enable_irq();
	SPIF_STATUS_WAIT_IDLE(SPIF_WAIT_IDLE_CYC);
    spif_wait_nobusy(SFLG_WELWIP, SPIF_TIMEOUT, ERR_BUSY);
	HAL_CACHE_EXIT_BYPASS_SECTION();
    if(cb == 0)
    {
        hal_cache_tag_flush();
    }
    spif_unlock(cs);
    return retval;
}

// /**
// * @fn void HalFlashLock(flash_lock_t lock)
// * @brief flash lock.
// * @param  none.
// * @return none
// */
// void HalFlashLock(flash_lock_t lock)
// {
//     REG_FLASH_WRITE_CTRL = WRITE_ENABLE;
//     REG_FLASH_BP = lock;
//     REG_FLASH_WRITE_CTRL = WRITE_PROTECT;
// }

// /**
// * @fn void HalFlashUnlock(void)
// * @brief flash unlock.
// * @param  none.
// * @return none
// */
// void HalFlashUnlock(void)
// {
//     while( _spif_read_status_reg_x() )
//     {
//         REG_FLASH_WRITE_CTRL = WRITE_ENABLE;
//         REG_FLASH_BP = FLASH_UNLOCK;
//         REG_FLASH_WRITE_CTRL = WRITE_PROTECT;
//         WaitUs(50);
//     }
// }


